// Copyright 2022-2023 Bloomberg Finance L.P.
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// mwcu_operationchain_cpp03.h                                        -*-C++-*-

// Automatically generated file.  **DO NOT EDIT**

#ifndef INCLUDED_MWCU_OPERATIONCHAIN_CPP03
#define INCLUDED_MWCU_OPERATIONCHAIN_CPP03

//@PURPOSE: Provide C++03 implementation for mwcu_operationchain.h
//
//@CLASSES: See mwcu_operationchain.h for list of classes
//
//@SEE_ALSO: mwcu_operationchain
//
//@DESCRIPTION:  This component is the C++03 translation of a C++11 component,
// generated by the 'sim_cpp11_features.pl' program.  If the original header
// contains any specially delimited regions of C++11 code, then this generated
// file contains the C++03 equivalent, i.e., with variadic templates expanded
// and rvalue-references replaced by 'bslmf::MovableRef' objects.  The header
// code in this file is designed to be '#include'd into the original header
// when compiling with a C++03 compiler.  If there are no specially delimited
// regions of C++11 code, then this header contains no code and is not
// '#include'd in the original header.
//
// Generated on Tue Jun 28 12:27:23 2022
// Command line: sim_cpp11_features.pl mwcu_operationchain.h

#ifdef COMPILING_MWCU_OPERATIONCHAIN_H

namespace BloombergLP {

// FORWARD DECLARATION
namespace bslma {
class Allocator;
}

namespace mwcu {

class OperationChain;
class OperationChainLink;
class OperationChain_Job;

// ====================================================
// struct OperationChain_IsCompletionCallbackCompatible
// ====================================================

template <class TYPE>
struct OperationChain_IsCompletionCallbackCompatible
#if __cplusplus >= 201703L
: bsl::integral_constant<
      bool,
      bsl::is_destructible_v<bsl::decay_t<TYPE> > &&
          bsl::is_move_constructible_v<bsl::decay_t<TYPE> > >
#else
: bsl::integral_constant<bool, true>
#endif
{
    // A metafunction returning whether or not the specified 'TYPE' satisfies
    // requirements for a completion callback. To satisfy those requirements
    // 'TYPE' must meet the requirements of Destructible and MoveConstructible
    // as specified in the C++ standard.
    //
    // Note that unless compiled with C++17 support, this metafunction always
    // returns 'true'.
};

// ===================================================
// struct OperationChain_IsOperationCallbackCompatible
// ===================================================

template <class TYPE>
struct OperationChain_IsOperationCallbackCompatible
#if __cplusplus >= 201703L
: bsl::integral_constant<
      bool,
      bsl::is_destructible_v<bsl::decay_t<TYPE> > &&
          bsl::is_move_constructible_v<bsl::decay_t<TYPE> > &&
          bsl::is_invocable_v<bsl::add_rvalue_reference_t<bsl::decay_t<TYPE> >,
                              mwcu::NoOp> >
#else
: bsl::integral_constant<bool, true>
#endif
{
    // A metafunction returning whether or not the specified 'TYPE' satisfies
    // requirements for a operation callback. To satisfy those requirements
    // 'TYPE' must meet the requirements of Destructible and MoveConstructible
    // as specified in the C++ standard, as well as be Invocable with a single
    // parameter that is a functor object of unspecified type taking an
    // arbitrary number of template arguments.
    //
    // Note that unless compiled with C++17 support, this metafunction always
    // returns 'true'.
};

// ==============================================
// class OperationChain_CompletionCallbackWrapper
// ==============================================

template <class CO_CALLBACK>
class OperationChain_CompletionCallbackWrapper {
    // A wrapper around a completion callback object that invokes the callback
    // and notifies the operation chain.

    // PRECONDITIONS
    BSLMF_ASSERT(
        OperationChain_IsCompletionCallbackCompatible<CO_CALLBACK>::value);

  public:
    // TYPES
    typedef void ResultType;
    // Defines the result type of the call operator.

    typedef bsl::list<OperationChain_Job>::iterator JobHandle;

  private:
    // PRIVATE DATA
    OperationChain* d_chain_p;

    JobHandle d_jobHandle;

    CO_CALLBACK* d_coCallback_p;

  public:
    // CREATORS
    OperationChain_CompletionCallbackWrapper(OperationChain* chain,
                                             JobHandle       jobHandle,
                                             CO_CALLBACK*    coCallback)
        BSLS_KEYWORD_NOEXCEPT;
    // Create a 'OperationChain_CompletionCallbackWrapper' object having
    // the associated operation 'chain', 'jobHandle' and 'coCallback'
    // completion callback.

  public:
    // ACCESSORS
#if BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
// {{{ BEGIN GENERATED CODE
// Command line: sim_cpp11_features.pl mwcu_operationchain.h
#ifndef MWCU_OPERATIONCHAIN_VARIADIC_LIMIT
#define MWCU_OPERATIONCHAIN_VARIADIC_LIMIT 9
#endif
#ifndef MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A
#define MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A MWCU_OPERATIONCHAIN_VARIADIC_LIMIT
#endif
#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 0
    void operator()() const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 0

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 1
    template <class ARGS_1>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 1

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 2
    template <class ARGS_1, class ARGS_2>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 2

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 3
    template <class ARGS_1, class ARGS_2, class ARGS_3>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 3

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 4
    template <class ARGS_1, class ARGS_2, class ARGS_3, class ARGS_4>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 4

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 5
    template <class ARGS_1,
              class ARGS_2,
              class ARGS_3,
              class ARGS_4,
              class ARGS_5>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 5

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 6
    template <class ARGS_1,
              class ARGS_2,
              class ARGS_3,
              class ARGS_4,
              class ARGS_5,
              class ARGS_6>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 6

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 7
    template <class ARGS_1,
              class ARGS_2,
              class ARGS_3,
              class ARGS_4,
              class ARGS_5,
              class ARGS_6,
              class ARGS_7>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_7) args_7) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 7

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 8
    template <class ARGS_1,
              class ARGS_2,
              class ARGS_3,
              class ARGS_4,
              class ARGS_5,
              class ARGS_6,
              class ARGS_7,
              class ARGS_8>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_7) args_7,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_8) args_8) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 8

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 9
    template <class ARGS_1,
              class ARGS_2,
              class ARGS_3,
              class ARGS_4,
              class ARGS_5,
              class ARGS_6,
              class ARGS_7,
              class ARGS_8,
              class ARGS_9>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_7) args_7,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_8) args_8,
                    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_9) args_9) const;
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_A >= 9

#else
    // The generated code below is a workaround for the absence of perfect
    // forwarding in some compilers.
    template <class... ARGS>
    void operator()(BSLS_COMPILERFEATURES_FORWARD_REF(ARGS)... args) const;
// }}} END GENERATED CODE
#endif

    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OperationChain_CompletionCallbackWrapper,
                                   bsl::is_nothrow_move_constructible)
};

// ========================
// class OperationChain_Job
// ========================

class OperationChain_Job {
    // A job representing an async operation to be executed and accounted for.

  public:
    // TYPES
    typedef bsl::list<OperationChain_Job>::iterator JobHandle;

  private:
    // PRIVATE TYPES
    class TargetBase {
        // An interface used to implement the type erasure technique.

      public:
        // CREATORS
        virtual ~TargetBase();
        // Destroy this object and the contained callback objects with it.

      public:
        // MANIPULATORS
        virtual void execute(OperationChain* chain, JobHandle jobHandle) = 0;
        // Start executing the associated async operation bound to the
        // contained operation callback. On completion, invoke the
        // contained completion callback and notify the specified operation
        // 'chain'.
    };

    template <class OP_CALLBACK, class CO_CALLBACK>
    class Target : public TargetBase {
        // Provides an implementation of the 'TargetBase' interface.

        // PRECONDITIONS
        BSLMF_ASSERT(
            OperationChain_IsOperationCallbackCompatible<OP_CALLBACK>::value);
        BSLMF_ASSERT(
            OperationChain_IsCompletionCallbackCompatible<CO_CALLBACK>::value);

      private:
        // PRIVATE DATA
        bslalg::ConstructorProxy<OP_CALLBACK> d_opCallback;
        bslalg::ConstructorProxy<CO_CALLBACK> d_coCallback;

      private:
        // NOT IMPLEMENTED
        Target(const Target&) BSLS_KEYWORD_DELETED;
        Target& operator=(const Target&) BSLS_KEYWORD_DELETED;

      public:
        // CREATORS
        template <class OP_CALLBACK_ARG, class CO_CALLBACK_ARG>
        Target(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK_ARG) opCallback,
               BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK_ARG) coCallback,
               bslma::Allocator* allocator);
        // Create a 'Target' object containing the specified 'opCallback'
        // operation callback and 'coCallback' completion callback. Specify
        // an 'allocator' used to supply memory.

      public:
        // MANIPULATORS
        void execute(OperationChain* chain,
                     JobHandle       jobHandle) BSLS_KEYWORD_OVERRIDE;
        // Implements 'TargetBase::execute'.
    };

    struct Dummy : public mwcu::NoOp {
        // A "small" dummy functor used to help calculate the size of the
        // on-stack buffer.

        void* d_padding[5];
    };

  private:
    // PRIVATE DATA
    unsigned d_id;
    // Job ID, used to tell the number of jobs in a link. This value is
    // unique per link, starts with 1, and is incremented with each new
    // job inserted into a link.

    mwcu::ObjectPlaceHolder<sizeof(Target<Dummy, Dummy>)> d_target;
    // Uses an on-stack buffer to allocate memory for "small" objects, and
    // falls back to requesting memory from the the supplied allocator if
    // the buffer is not large enough. Note that the size of the on-stack
    // buffer is an arbitrary value.

  private:
    // NOT IMPLEMENTED
    OperationChain_Job(const OperationChain_Job&) BSLS_KEYWORD_DELETED;
    OperationChain_Job&
    operator=(const OperationChain_Job&) BSLS_KEYWORD_DELETED;

  public:
    // CREATORS
    template <class OP_CALLBACK, class CO_CALLBACK>
    explicit OperationChain_Job(unsigned id,
                                BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK)
                                    opCallback,
                                BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK)
                                    coCallback,
                                bslma::Allocator* allocator);
    // Create a 'OperationChain_Job' object having the specified 'id' and
    // containing the specified 'opCallback' operation callback and
    // 'coCallback' completion callback. Specify an 'allocator' used to
    // supply memory.
    //
    // 'bsl::decay_t<OP_CALLBACK>' and 'bsl::decay_t<CO_CALLBACK>' must
    // meet the requirements of Destructible and MoveConstructible as
    // specified in the C++ standard. Given an object 'f1' of type
    // 'bsl::decay_t<OP_CALLBACK>&&', 'f1(f2)' shall be a valid
    // expression, where 'f2' is a function object of unspecified type
    // callable with the same arguments as 'bsl::decay_t<CO_CALLBACK>&&'.

    ~OperationChain_Job();
    // Destroy this object and the contained callback objects with it.

  public:
    // MANIPULATORS
    void execute(OperationChain* chain, JobHandle jobHandle) BSLS_NOTHROW_SPEC;
    // Start executing the associated async operation bound to the
    // contained operation callback. On completion, invoke the contained
    // completion callback and notify the specified operation 'chain' using
    // the specified 'jobHandle'.

  public:
    // ACCESSORS
    unsigned id() const BSLS_KEYWORD_NOEXCEPT;
    // Return the ID of this job.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OperationChain_Job,
                                   bslma::UsesBslmaAllocator)
};

// ====================
// class OperationChain
// ====================

class OperationChain {
    // A mechanism to serialize execution of async operations.

  public:
    // TYPES
    typedef OperationChainLink Link;

  private:
    // PRIVATE TYPES
    typedef OperationChain_Job Job;

    typedef bsl::list<Job> JobList;

    typedef JobList::iterator JobHandle;

  private:
    // PRIVATE DATA
    mutable bslmt::Mutex d_mutex;
    // Used for general thread-safety.

    mutable bslmt::Condition d_condition;
    // Used to synchronize with completion of async operations.

    bool d_isStarted;
    // Whether or not this operation chain is started.

    unsigned d_numLinks;
    // The number of links in this operation chain.

    unsigned d_numJobsRunning;
    // The number of jobs currently executing.

    JobList d_jobList;
    // Jobs executing async operations.

    // FRIENDS
    template <class>
    friend class OperationChain_CompletionCallbackWrapper;

  private:
    // PRIVATE MANIPULATORS
    void onOperationCompleted(JobHandle handle) BSLS_KEYWORD_NOEXCEPT;
    // Callback invoked to notify this operation chain an async
    // operation identified by the specified 'handle' has completed.

    void run() BSLS_KEYWORD_NOEXCEPT;
    // Execute all operations in the first link of this operation chain.

  private:
    // NOT IMPLEMENTED
    OperationChain(const OperationChain&) BSLS_KEYWORD_DELETED;
    OperationChain& operator=(const OperationChain&) BSLS_KEYWORD_DELETED;

  public:
    // CREATORS
    explicit OperationChain(bslma::Allocator* basicAllocator = 0);
    explicit OperationChain(bool              createStarted,
                            bslma::Allocator* basicAllocator = 0);
    // Create a 'OperationChain' object. Optionally specify a
    // 'createStarted' flag used to create the chain in a started state. If
    // the flag is not specified or its value is 'false', create the chain
    // in a stopped state. Optionally specify a 'basicAllocator' used to
    // supply memory. If 'basicAllocator' is 0, the default memory
    // allocator is used.

    ~OperationChain();
    // Destroy this object. Call 'stop()' followed by 'join()'. Destroy all
    // operation and completion callback objects left in the chain, if any.

  public:
    // MANIPULATORS
    void start();
    // Start executing operations in this operation chain. If the chain is
    // already started, this function has no effect.
    //
    // This function meets the strong exception guarantee. If an exception
    // is thrown, this function has no effect.

    void stop() BSLS_KEYWORD_NOEXCEPT;
    // Stop executing operations in this operation chain without blocking
    // the calling thread pending completion of any currently executing
    // operation. If the chain is already stopped, this function has no
    // effect.

    void join() BSLS_KEYWORD_NOEXCEPT;
    // Block the calling thread until there is no operations executing
    // in this operation chain. If there is no operations currently
    // executing, return immediately.

    void append(Link* link);
    void append(bslmf::MovableRef<Link> link);
    // Append the specified 'link' to this operation chain. If the chain is
    // started and the appended 'link' is the first one in the chain,
    // execute all operations in the appended 'link' immediately. If the
    // appended 'link' is empty, this function has no effect. After this
    // operation completes, 'link' is left empty. The behavior is undefined
    // unless 'link' uses the same allocator as this object.
    //
    // This function meets the strong exception guarantee. If an exception
    // is thrown, this function has no effect.

#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS
    void append(bsl::initializer_list<Link*> links);
    // Append all links from the specified 'links' list to this operation
    // chain in the order they appear in the list. If the chain is started
    // and the first appended link is the first one in the chain, execute
    // all operations in the first appended link immediately. If the
    // 'links' list is empty, this function has no effect. If the 'links'
    // list contains empty links, ignore them. After this operation
    // completes, all links in the 'links' list are left empty. The
    // behavior is undefined unless all links in the 'links' list use the
    // same allocator as this object.
    //
    // This function meets the strong exception guarantee. If an exception
    // is thrown, this function has no effect.
#endif

    void append(Link* const* links, size_t count);
    // Append the specified 'count' number of links from the specified
    // 'links' array to this operation chain in the order they appear in
    // the array. If the chain is started and the first appended link is
    // the first one in the chain, execute all operations in the first
    // appended link immediately. If 'count' is 0, this function has no
    // effect. If the 'links' array contains empty links, ignore them.
    // After this operation completes, all links in the 'links' array are
    // left empty. The behavior is undefined unless all links in the
    // 'links' array use the same allocator as this object.
    //
    // This function meets the strong exception guarantee. If an exception
    // is thrown, this function has no effect.

    template <class OP_CALLBACK>
    void appendInplace(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK)
                           opCallback);
    template <class OP_CALLBACK, class CO_CALLBACK>
    void appendInplace(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK)
                           opCallback,
                       BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK)
                           coCallback);
    // Append a link containing a single operation represented by the
    // specified 'opCallback' operation callback and the optionally
    // specified 'coCallback' completion callback to this operation chain.
    // If no completion callback is specified, use 'mwcu::NoOp'. If the
    // chain is started and the appended link is the first one in the
    // chain, execute the single operation in the appended link
    // immediately.
    //
    // This function meets the strong exception guarantee. If an exception
    // is thrown, this function has no effect.
    //
    // 'bsl::decay_t<OP_CALLBACK>' and 'bsl::decay_t<CO_CALLBACK>' must
    // meet the requirements of Destructible and MoveConstructible as
    // specified in the C++ standard. Given an object 'f1' of type
    // 'bsl::decay_t<OP_CALLBACK>&&', 'f1(f2)' shall be a valid
    // expression, where 'f2' is a function object of unspecified type
    // callable with the same arguments as 'bsl::decay_t<CO_CALLBACK>&&'.

    int popBack() BSLS_KEYWORD_NOEXCEPT;
    int popBack(Link* link) BSLS_KEYWORD_NOEXCEPT;
    // Extract and remove the last link from this operation chain and load
    // it into the optionally specified 'link'. Return 0 on success, and a
    // non-zero value if the link can not be extracted, either because the
    // chain is empty, or because operations in the extracted link are
    // currently executing. On failure, this function has no effect and the
    // contents of 'link' are unmodified. The behavior is undefined unless
    // 'link' uses that same allocator as this object.

    size_t removeAll() BSLS_KEYWORD_NOEXCEPT;
    // Remove all links from this operation chain. If operations in this
    // chain are currently executing, do not remove the first link. Return
    // the number of operations removed. If this chain is empty, this
    // function has no effect and 0 is returned.

  public:
    // ACCESSORS
    bool isStarted() const BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if this operation chain is started, and 'false'
    // otherwise. Note that a stopped chain can still be executing
    // operations.

    bool isRunning() const BSLS_KEYWORD_NOEXCEPT;
    // Return 'true' if this operation chain is currently executing at
    // least one operation, and 'false' otherwise.

    size_t numLinks() const BSLS_KEYWORD_NOEXCEPT;
    // Return the number of links in this operation chain. A value of 0
    // means the chain is empty.

    size_t numOperations() const BSLS_KEYWORD_NOEXCEPT;
    // Return the number of operations in this operation chain, including
    // those that are currently executing (if any). A value of 0 means the
    // chain is empty.

    size_t numOperationsPending() const BSLS_KEYWORD_NOEXCEPT;
    // Return the number of operations in this operation chain that are not
    // currently executing.

    size_t numOperationsExecuting() const BSLS_KEYWORD_NOEXCEPT;
    // Return the number of operations in this operation chain that are
    // currently executing.

    bslma::Allocator* allocator() const BSLS_KEYWORD_NOEXCEPT;
    // Return the allocator used by this operation chain to supply memory.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OperationChain, bslma::UsesBslmaAllocator)
};

// ========================
// class OperationChainLink
// ========================

class OperationChainLink {
    // A representation of a link in the operation chain.

  private:
    // PRIVATE TYPES
    typedef OperationChain_Job Job;

    typedef bsl::list<Job> JobList;

  private:
    // PRIVATE DATA
    JobList d_jobList;
    // Jobs executing async operations.

    // FRIENDS
    friend class OperationChain;

  private:
    // NOT IMPLEMENTED
    OperationChainLink(const OperationChainLink&) BSLS_KEYWORD_DELETED;
    OperationChainLink&
    operator=(const OperationChainLink&) BSLS_KEYWORD_DELETED;

  public:
    // CREATORS
    explicit OperationChainLink(bslma::Allocator* basicAllocator = 0);
    // Create a 'OperationChainLink' object initialized empty. Optionally
    // specify a 'basicAllocator' used to supply memory. If
    // 'basicAllocator' is 0, the default memory allocator is used.

    OperationChainLink(bslmf::MovableRef<OperationChainLink> original);
    // Create a 'OperationChainLink' object having the same value as the
    // specified 'original' object by moving the contents of 'original' to
    // the new link. The allocator associated with 'original' is propagated
    // for use in the newly-created link. 'original' is left empty.

  public:
    // MANIPULATORS
    OperationChainLink&
    operator=(bslmf::MovableRef<OperationChainLink> rhs) BSLS_KEYWORD_NOEXCEPT;
    // Assign to this object the value of the specified 'rhs' object and
    // return a reference providing modifiable access to this object. The
    // contents of 'rhs' are moved to this link. 'rhs' is left empty. The
    // behavior is undefined unless 'rhs' uses that same allocator as this
    // object.

    template <class OP_CALLBACK>
    void insert(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK) opCallback);
    template <class OP_CALLBACK, class CO_CALLBACK>
    void insert(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK) opCallback,
                BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK) coCallback);
    // Insert an operation into this link. Specify a 'opCallback' operation
    // callback initiating the operation. Optionally specify a 'coCallback'
    // completion callback to be passed to the operation callback as a
    // parameter. If no completion callback is specified, use 'mwcu::NoOp'.
    //
    // This function meets the strong exception guarantee. If an exception
    // is thrown, this function has no effect.
    //
    // 'bsl::decay_t<OP_CALLBACK>' and 'bsl::decay_t<CO_CALLBACK>' must
    // meet the requirements of Destructible and MoveConstructible as
    // specified in the C++ standard. Given an object 'f1' of type
    // 'bsl::decay_t<OP_CALLBACK>&&', 'f1(f2)' shall be a valid
    // expression, where 'f2' is a function object of unspecified type
    // callable with the same arguments as 'bsl::decay_t<CO_CALLBACK>&&'.

    size_t removeAll() BSLS_KEYWORD_NOEXCEPT;
    // Remove all operations in this link, if any. Return the number of
    // operations removed.

    void swap(OperationChainLink& other) BSLS_KEYWORD_NOEXCEPT;
    // Swap the contents of this object and the specified 'other' object.
    // The behavior is undefined unless 'other' uses that same allocator
    // as this object.

  public:
    // ACCESSORS
    size_t numOperations() const BSLS_KEYWORD_NOEXCEPT;
    // Return the number of operations in this link. A value of 0 means the
    // link is empty.

    bslma::Allocator* allocator() const BSLS_KEYWORD_NOEXCEPT;
    // Return the allocator used by this link to supply memory.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OperationChainLink,
                                   bslma::UsesBslmaAllocator)
};

// FREE OPERATORS
void swap(OperationChainLink& lhs,
          OperationChainLink& rhs) BSLS_KEYWORD_NOEXCEPT;
// Swap the contents of 'lhs' and 'rhs'. The behavior is undefined unless
// 'lhs' and 'rhs' use the same allocator.

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

// ----------------------------------------------
// class OperationChain_CompletionCallbackWrapper
// ----------------------------------------------

// CREATORS
template <class CO_CALLBACK>
inline OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::
    OperationChain_CompletionCallbackWrapper(OperationChain* chain,
                                             JobHandle       jobHandle,
                                             CO_CALLBACK*    coCallback)
        BSLS_KEYWORD_NOEXCEPT : d_chain_p(chain),
                                d_jobHandle(jobHandle),
                                d_coCallback_p(coCallback)
{
    // PRECONDITIONS
    BSLS_ASSERT(chain);
    BSLS_ASSERT(coCallback);
}

// ACCESSORS
#if BSLS_COMPILERFEATURES_SIMULATE_VARIADIC_TEMPLATES
// {{{ BEGIN GENERATED CODE
// Command line: sim_cpp11_features.pl mwcu_operationchain.h
#ifndef MWCU_OPERATIONCHAIN_VARIADIC_LIMIT
#define MWCU_OPERATIONCHAIN_VARIADIC_LIMIT 9
#endif
#ifndef MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B
#define MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B MWCU_OPERATIONCHAIN_VARIADIC_LIMIT
#endif
#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 0
template <class CO_CALLBACK>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(

) const
{
    try {
        bslmf::Util::moveIfSupported((*d_coCallback_p))();
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 0

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 1
template <class CO_CALLBACK>
template <class ARGS_1>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1) const
{
    try {
        bslmf::Util::moveIfSupported((*d_coCallback_p))(
            bslmf::Util::forward<ARGS_1>(args_1));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 1

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 2
template <class CO_CALLBACK>
template <class ARGS_1, class ARGS_2>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 2

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 3
template <class CO_CALLBACK>
template <class ARGS_1, class ARGS_2, class ARGS_3>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 3

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 4
template <class CO_CALLBACK>
template <class ARGS_1, class ARGS_2, class ARGS_3, class ARGS_4>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3),
                               bslmf::Util::forward<ARGS_4>(args_4));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 4

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 5
template <class CO_CALLBACK>
template <class ARGS_1, class ARGS_2, class ARGS_3, class ARGS_4, class ARGS_5>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3),
                               bslmf::Util::forward<ARGS_4>(args_4),
                               bslmf::Util::forward<ARGS_5>(args_5));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 5

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 6
template <class CO_CALLBACK>
template <class ARGS_1,
          class ARGS_2,
          class ARGS_3,
          class ARGS_4,
          class ARGS_5,
          class ARGS_6>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3),
                               bslmf::Util::forward<ARGS_4>(args_4),
                               bslmf::Util::forward<ARGS_5>(args_5),
                               bslmf::Util::forward<ARGS_6>(args_6));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 6

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 7
template <class CO_CALLBACK>
template <class ARGS_1,
          class ARGS_2,
          class ARGS_3,
          class ARGS_4,
          class ARGS_5,
          class ARGS_6,
          class ARGS_7>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_7) args_7) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3),
                               bslmf::Util::forward<ARGS_4>(args_4),
                               bslmf::Util::forward<ARGS_5>(args_5),
                               bslmf::Util::forward<ARGS_6>(args_6),
                               bslmf::Util::forward<ARGS_7>(args_7));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 7

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 8
template <class CO_CALLBACK>
template <class ARGS_1,
          class ARGS_2,
          class ARGS_3,
          class ARGS_4,
          class ARGS_5,
          class ARGS_6,
          class ARGS_7,
          class ARGS_8>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_7) args_7,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_8) args_8) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3),
                               bslmf::Util::forward<ARGS_4>(args_4),
                               bslmf::Util::forward<ARGS_5>(args_5),
                               bslmf::Util::forward<ARGS_6>(args_6),
                               bslmf::Util::forward<ARGS_7>(args_7),
                               bslmf::Util::forward<ARGS_8>(args_8));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 8

#if MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 9
template <class CO_CALLBACK>
template <class ARGS_1,
          class ARGS_2,
          class ARGS_3,
          class ARGS_4,
          class ARGS_5,
          class ARGS_6,
          class ARGS_7,
          class ARGS_8,
          class ARGS_9>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_1) args_1,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_2) args_2,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_3) args_3,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_4) args_4,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_5) args_5,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_6) args_6,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_7) args_7,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_8) args_8,
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS_9) args_9) const
{
    try {
        bslmf::Util::moveIfSupported(
            (*d_coCallback_p))(bslmf::Util::forward<ARGS_1>(args_1),
                               bslmf::Util::forward<ARGS_2>(args_2),
                               bslmf::Util::forward<ARGS_3>(args_3),
                               bslmf::Util::forward<ARGS_4>(args_4),
                               bslmf::Util::forward<ARGS_5>(args_5),
                               bslmf::Util::forward<ARGS_6>(args_6),
                               bslmf::Util::forward<ARGS_7>(args_7),
                               bslmf::Util::forward<ARGS_8>(args_8),
                               bslmf::Util::forward<ARGS_9>(args_9));
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
#endif  // MWCU_OPERATIONCHAIN_VARIADIC_LIMIT_B >= 9

#else
// The generated code below is a workaround for the absence of perfect
// forwarding in some compilers.
template <class CO_CALLBACK>
template <class... ARGS>
inline void OperationChain_CompletionCallbackWrapper<CO_CALLBACK>::operator()(
    BSLS_COMPILERFEATURES_FORWARD_REF(ARGS)... args) const
{
    try {
        bslmf::Util::moveIfSupported((*d_coCallback_p))(
            bslmf::Util::forward<ARGS>(args)...);
    }
    catch (...) {
        d_chain_p->onOperationCompleted(d_jobHandle);
        throw;
    }

    d_chain_p->onOperationCompleted(d_jobHandle);
}
// }}} END GENERATED CODE
#endif

// ------------------------
// class OperationChain_Job
// ------------------------

// CREATORS
template <class OP_CALLBACK, class CO_CALLBACK>
inline OperationChain_Job::OperationChain_Job(
    unsigned id,
    BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK) opCallback,
    BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK) coCallback,
    bslma::Allocator* allocator)
: d_id(id)
{
    // PRECONDITIONS
    BSLS_ASSERT(allocator);

    typedef Target<typename bsl::decay<OP_CALLBACK>::type,
                   typename bsl::decay<CO_CALLBACK>::type>
        Target;

    d_target.createObject<Target>(
        allocator,
        BSLS_COMPILERFEATURES_FORWARD(OP_CALLBACK, opCallback),
        BSLS_COMPILERFEATURES_FORWARD(CO_CALLBACK, coCallback),
        allocator);
}

// CREATORS
inline OperationChain_Job::~OperationChain_Job()
{
    d_target.deleteObject<TargetBase>();
}

// MANIPULATORS
inline void OperationChain_Job::execute(OperationChain* chain,
                                        JobHandle jobHandle) BSLS_NOTHROW_SPEC
{
    // NOTE: 'BSLS_NOTHROW_SPEC' specification ensures that 'bsl::terminate' is
    //       called in case of exception in C++03.

    // PRECONDITIONS
    BSLS_ASSERT(chain);

    d_target.object<TargetBase>()->execute(chain, jobHandle);
}

// ACCESSORS
inline unsigned OperationChain_Job::id() const BSLS_KEYWORD_NOEXCEPT
{
    return d_id;
}

// --------------------------------
// class OperationChain_Job::Target
// --------------------------------

// CREATORS
template <class OP_CALLBACK, class CO_CALLBACK>
template <class OP_CALLBACK_ARG, class CO_CALLBACK_ARG>
inline OperationChain_Job::Target<OP_CALLBACK, CO_CALLBACK>::Target(
    BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK_ARG) opCallback,
    BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK_ARG) coCallback,
    bslma::Allocator* allocator)
: d_opCallback(BSLS_COMPILERFEATURES_FORWARD(OP_CALLBACK_ARG, opCallback),
               allocator)
, d_coCallback(BSLS_COMPILERFEATURES_FORWARD(CO_CALLBACK_ARG, coCallback),
               allocator)
{
    // PRECONDITIONS
    BSLMF_ASSERT(
        OperationChain_IsOperationCallbackCompatible<OP_CALLBACK_ARG>::value);
    BSLMF_ASSERT(
        OperationChain_IsCompletionCallbackCompatible<CO_CALLBACK_ARG>::value);
    BSLS_ASSERT(allocator);
}

// MANIPULATORS
template <class OP_CALLBACK, class CO_CALLBACK>
inline void OperationChain_Job::Target<OP_CALLBACK, CO_CALLBACK>::execute(
    OperationChain* chain,
    JobHandle       jobHandle)
{
    // PRECONDITIONS
    BSLS_ASSERT(chain);

    typedef OperationChain_CompletionCallbackWrapper<CO_CALLBACK> CoCbWrapper;

    bslmf::Util::moveIfSupported(d_opCallback.object())(
        CoCbWrapper(chain, jobHandle, &d_coCallback.object()));
}

// --------------------
// class OperationChain
// --------------------

// MANIPULATORS
inline void OperationChain::append(Link* link)
{
    append(&link, 1);
}

inline void OperationChain::append(bslmf::MovableRef<Link> link)
{
    Link* link_p = &bslmf::MovableRefUtil::access(link);
    append(&link_p, 1);
}

#ifdef BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS
inline void OperationChain::append(bsl::initializer_list<Link*> links)
{
    append(links.begin(), links.size());
}
#endif  // BSLS_COMPILERFEATURES_SUPPORT_GENERALIZED_INITIALIZERS

template <class OP_CALLBACK>
inline void
OperationChain::appendInplace(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK)
                                  opCallback)
{
    // PRECONDITIONS
    BSLMF_ASSERT(
        OperationChain_IsOperationCallbackCompatible<OP_CALLBACK>::value);

    appendInplace(BSLS_COMPILERFEATURES_FORWARD(OP_CALLBACK, opCallback),
                  NoOp());
}

template <class OP_CALLBACK, class CO_CALLBACK>
inline void OperationChain::appendInplace(
    BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK) opCallback,
    BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK) coCallback)
{
    // PRECONDITIONS
    BSLMF_ASSERT(
        OperationChain_IsOperationCallbackCompatible<OP_CALLBACK>::value);
    BSLMF_ASSERT(
        OperationChain_IsCompletionCallbackCompatible<CO_CALLBACK>::value);

    // create a link
    Link link(allocator());
    link.insert(BSLS_COMPILERFEATURES_FORWARD(OP_CALLBACK, opCallback),
                BSLS_COMPILERFEATURES_FORWARD(CO_CALLBACK, coCallback));

    // append it
    append(&link);
}

// ------------------------
// class OperationChainLink
// ------------------------

// MANIPULATORS
template <class OP_CALLBACK>
inline void
OperationChainLink::insert(BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK)
                               opCallback)
{
    // PRECONDITIONS
    BSLMF_ASSERT(
        OperationChain_IsOperationCallbackCompatible<OP_CALLBACK>::value);

    insert(BSLS_COMPILERFEATURES_FORWARD(OP_CALLBACK, opCallback), NoOp());
}

template <class OP_CALLBACK, class CO_CALLBACK>
inline void OperationChainLink::insert(
    BSLS_COMPILERFEATURES_FORWARD_REF(OP_CALLBACK) opCallback,
    BSLS_COMPILERFEATURES_FORWARD_REF(CO_CALLBACK) coCallback)
{
    // PRECONDITIONS
    BSLMF_ASSERT(
        OperationChain_IsOperationCallbackCompatible<OP_CALLBACK>::value);
    BSLMF_ASSERT(
        OperationChain_IsCompletionCallbackCompatible<CO_CALLBACK>::value);

    // create a job
    d_jobList.emplace_front(
        d_jobList.size() + 1,  // id
        BSLS_COMPILERFEATURES_FORWARD(OP_CALLBACK, opCallback),
        BSLS_COMPILERFEATURES_FORWARD(CO_CALLBACK, coCallback));
}

}  // close package namespace

// FREE OPERATORS
inline void mwcu::swap(OperationChainLink& lhs,
                       OperationChainLink& rhs) BSLS_KEYWORD_NOEXCEPT
{
    lhs.swap(rhs);
}

}  // close enterprise namespace

#else  // if ! defined(DEFINED_MWCU_OPERATIONCHAIN_H)
#error Not valid except when included from mwcu_operationchain.h
#endif  // ! defined(COMPILING_MWCU_OPERATIONCHAIN_H)

#endif  // ! defined(INCLUDED_MWCU_OPERATIONCHAIN_CPP03)

// ----------------------------------------------------------------------------
// Copyright 2022-2023 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
